package Agents;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.*;
import jade.domain.*;
import jade.domain.FIPAAgentManagement.*;
import jade.lang.acl.ACLMessage;
import AOP.Printer;
import Messages.AMessage;
import Messages.NotUnderstoodMessage;
import Messages.AMessage.MessageType;
import Messages.AMessage.VacuumStatus;

@SuppressWarnings("serial")
public class VacuumCleaner extends Messageable
{
	private boolean clean = false;
	private String currentJob = "";
	private int powerRemaining = 100;
	public VacuumStatus status = VacuumStatus.IDLE;//CHARGING;
	private int speed = 1;
	public int timeToUseOnTask;

	protected void setup()
	{
		DFAgentDescription dfd = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();   
		sd.setType(AMessage.GlobalNames.VACUUM_CLEANER_AGENT.get()); 
		sd.setName(getName());
		sd.setOwnership("VacuumCleaner");
		dfd.setName(getAID());
		dfd.addServices(sd);

		try { DFService.register(this,dfd); }
		catch (FIPAException e)
		{ System.err.println(getLocalName() + " registration with DF unsucceeded. Reason:  " + e.getMessage()); }

		speed = (int) (Math.random() * 10);
		if (speed == 0)
			speed = 1;

		HandleVacuumCleanerMessagesBehaviour messageHandlerBehaviour = new HandleVacuumCleanerMessagesBehaviour();
		addBehaviour(messageHandlerBehaviour);
		CleanFloorBehaviour cleanFloorBehaviour = new CleanFloorBehaviour(this, (int)(AMessage.unitsManipulatingConstant / 1.5));
		addBehaviour(cleanFloorBehaviour);
	}

	private class HandleVacuumCleanerMessagesBehaviour extends CyclicBehaviour
	{
		public void action()
		{
			ACLMessage  msg = myAgent.receive();
			boolean sendReply = false;
			if(msg != null)
			{
				ACLMessage reply = msg.createReply();
				AMessage message = null;
				try
				{
					message = AMessage.retrieveMessage(msg.getContent());
				}
				catch(Exception e)
				{
					Printer.error(msg);
				}
				if (AMessage.GlobalNames.decode(message.getSender()) == AMessage.GlobalNames.OFFICE_REGION_AGENT)
				{
					if (msg.getPerformative() == ACLMessage.CFP)
					{
						if (status == VacuumStatus.IDLE)
						{
							int workload = ((Messages.VacuumCleanerMessage)message).dirtyPct;
							timeToUseOnTask = (workload / speed) * (powerRemaining/5);
							reply.setPerformative(ACLMessage.PROPOSE);
							String con = AMessage.constructMessage((Messageable)myAgent, AMessage.MessageType.PROPOSE_CLEAN);
							reply.setContent(con);
							sendReply = true;
						}
					}
					else if (msg.getPerformative() == ACLMessage.INFORM)
					{
						if (message.getMessageType() == MessageType.NOTIFY_ONLY);
						{
							sendReply = false;
							clean = false;
							currentJob = "";
							status = VacuumStatus.IDLE;
						}
					}
					else if (msg.getPerformative() == ACLMessage.ACCEPT_PROPOSAL)
					{
						if (message.getMessageType() == MessageType.NOTIFY_ONLY);
						{
							clean = true;
							currentJob = message.getSender();
							sendReply = false;
							status = VacuumStatus.VACUUMING;
						}
					}
				}
				else if(AMessage.GlobalNames.decode(message.getSender()) == AMessage.GlobalNames.BIN_AGENT)
				{
					if (msg.getPerformative() == ACLMessage.CFP)
					{
						if (status == VacuumStatus.IDLE)
						{
							int workload = 0;
							if (((Messages.BinMessage)message).weight > ((Messages.BinMessage)message).volume)
								workload = ((Messages.BinMessage)message).weight;
							else
								workload = ((Messages.BinMessage)message).volume;
							
							timeToUseOnTask = speed*20/workload;
							reply.setPerformative(ACLMessage.PROPOSE);
							String con = AMessage.constructMessage((Messageable)myAgent, AMessage.MessageType.PROPOSE_EMPTY_BIN);
							reply.setContent(con);
							sendReply = true;
						}
					}
					else if (msg.getPerformative() == ACLMessage.ACCEPT_PROPOSAL)
					{
						if (message.getMessageType() == MessageType.NOTIFY_ONLY);
						{
							clean = true;
							currentJob = message.getSender();
							sendReply = false;
							status = VacuumStatus.EMPTING_BIN;
							Printer.Print(myAgent.getLocalName(), status.name(), powerRemaining);
						}
					}
					else if (msg.getPerformative() == ACLMessage.INFORM)
					{
						if (message.getMessageType() == MessageType.NOTIFY_ONLY);
						{
							clean = false;
							currentJob = "";
							sendReply = false;
							status = VacuumStatus.IDLE;
						}
					}
				}
				else 
				{
					reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
					reply.setContent(NotUnderstoodMessage.constructSpecialMessage((Messageable)myAgent, message));
					sendReply = true;
				}
				if (sendReply)
				{
					myAgent.send(reply);
				}
			}
		}
	}

	private class CleanFloorBehaviour extends TickerBehaviour
	{
		public CleanFloorBehaviour(Agent agent, long period)
		{
			super(agent, period);
		}

		protected void onTick()
		{
			if (clean)
			{
				powerRemaining = powerRemaining - 1;

				if (powerRemaining < 5)
					status = AMessage.VacuumStatus.NEED_POWER;
				ACLMessage message = new ACLMessage( ACLMessage.INFORM );
				message.addReceiver(new AID(currentJob, AID.ISLOCALNAME));
				String con = AMessage.constructMessage((Messageable)myAgent, AMessage.MessageType.VACUUM_CLEANER);
				message.setContent(con);
				myAgent.send(message);
			}
			if (status == AMessage.VacuumStatus.NEED_POWER)
			{
				clean = false;
				status = AMessage.VacuumStatus.CHARGING;
				String tempOR = currentJob;
				currentJob = "";

				ACLMessage message = new ACLMessage( ACLMessage.INFORM );
				message.addReceiver(new AID(tempOR, AID.ISLOCALNAME));
				String con = AMessage.constructMessage((Messageable)myAgent, AMessage.MessageType.I_NEED_POWER);
				message.setContent(con);
				myAgent.send(message);
			}
			else if(status == AMessage.VacuumStatus.CHARGING)
			{
				clean = false;
				currentJob = "";
				status = AMessage.VacuumStatus.CHARGING;
				powerRemaining = powerRemaining + 2;

				if (powerRemaining < 100)
					powerRemaining = powerRemaining + 1;
				if (powerRemaining >= 99)
					status = VacuumStatus.IDLE;
			}
			else if(status == VacuumStatus.IDLE)
			{
				if(powerRemaining < 25)
				{
					status = VacuumStatus.NEED_POWER;
				}
			}
			Printer.Print(myAgent.getLocalName(), status.name(), powerRemaining);
		}
	}
}
